package com.gosuncn.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.gosuncn.pojo.JobBean;
import com.gosuncn.tools.R;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.ui.Model;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;



/***
 *文件上传下载
 * @author zxl
 * @date 2021/7/26
*/
@RestController
@RequestMapping("/file")
public class FileController {


    private final static Logger log = LoggerFactory.getLogger(FileController.class);

    @Autowired
    private RedisTemplate redisTemplate;
    private static  final  String A = ".";


    /**
     * bin文件存放路径（jar同级目录）
     */
    @Value("${bin.save.path:./config/}")
    private String binSavePath;

    /**
     * 单文件上传
     * @param file  文件
     * @return
     * @throws IOException
     */
    @PostMapping( "/upload")
    public R testfileupload(@RequestParam("file") MultipartFile file,
                                 HttpSession session) throws IOException {
        try {
            // 判断文件不为空
            if (!file.isEmpty()) {
                // 获取文件原名称
                String oldFilename = file.getOriginalFilename();
                // 判断文件后缀不为空  字符串中没有找到. 返回-1
                if (oldFilename.lastIndexOf(A)==-1) {
                    return R.error().message("文件无后缀");
                }
                // 获取文件后缀
                String extension =oldFilename.substring(oldFilename.lastIndexOf(A));
                // 生成新的文件名称
                String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) +
                        UUID.randomUUID().toString().replace("-", "") + extension;
                // 文件上传路径
                String realPath = ResourceUtils.getURL("classpath:").getPath() + "/static/files";
                // 总路径
                String dateFormat = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
                String dateDirPath = realPath + "/" + dateFormat;
                // 读取路径文件
                File dataDir = new File(dateDirPath);
                // 文件为空
                if (!dataDir.exists()) {
                    // mkdirs创建多层文件
                    dataDir.mkdirs();
                }
                file.transferTo(new File(dataDir,newFileName));
                session.setAttribute("newFileName",newFileName);
                session.setAttribute("dateFormat",dateFormat);
                String path = dateDirPath+'/'+newFileName;
                return R.ok().message("文件上传成功").data("url",path);
//            return "redirect:/index";
            } else {
                return R.error().message("文件上传为空");
            }

        } catch (IOException | IllegalStateException e) {
            e.printStackTrace();
        }
        return R.error().message("文件上传失败");
    }


    /**
     * 上传多个文件
     * @param myfiles
     * @return
     * @throws IOException
     */
    @PostMapping("/uploadFiles")
    public R arrayFiles(@RequestParam("myfiles")  MultipartFile[] myfiles) {
        // 创建list列表存储返回上传文件的url；
        List<Map<String ,Object>> list = new ArrayList<>();
        for (int i=0; i<myfiles.length;i++) {
            // 判断文件不为空
            if (!myfiles[i].isEmpty()) {
                try {
                    String fileName = myfiles[i].getOriginalFilename();
                    // 判断文件后缀不为空  字符串中没有找到. 返回-1
                    if (fileName.lastIndexOf(A)==-1) {
                        return R.error().message("文件无后缀");
                    }
                    String extension =fileName.substring(fileName.lastIndexOf(A));
                    // 新文件名
                    String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())+UUID.randomUUID().toString().replace("-","")+extension;
                    // 上传文件路径
                    String realPath = ResourceUtils.getURL("classpath:").getPath()+"/static/files";
                    String dateFormat = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
                    String dateDirPath = realPath+"/"+dateFormat;
                    // 去读取这个目录
                    File file = new File(dateDirPath);
                    // 检测路径名文件或目录是否存在
                    if (!file.exists()) {
                        //创建目录
                        file.mkdirs();
                    }
                    // 创建一个map集合
                    HashMap<String, Object> map = new HashMap<>(myfiles.length);
                    // 存入map
                    map.put("url",dateDirPath+'/'+newFileName);
                    // 添加到list
                    list.add(map);
                    // 上传
                    myfiles[i].transferTo(new File(dateDirPath,newFileName));
                } catch (IOException | IllegalStateException e) {
                    e.printStackTrace();
                    i++;
                    return R.error().message("第"+i+"上传失败");
                }
            } else {
                // i+1原本是指，原本存入数组默认0为第一个文件，所以便于区别+1
                i++;
                return R.error().message("第"+i+"个文件上传为空");
            }
        }
            return R.ok().message("文件上传成功").data("path",list.toString());
    }





    /**
     * 文件本地下载
     * @param openStyle   attachment下载  inline在线打开
     * @param path        下载文件路径
     * @param response
     */

    @GetMapping("/download")
    public R downloadFile(String openStyle, HttpServletResponse response,
                      @RequestParam("path") String path) throws IOException {
        InputStream is = null;
        OutputStream os = null;
        // 缓冲区输入流
        BufferedInputStream bis = null;
        try {
            // attachment下载  inline在线打开
            // 获取打开方式  openSytle为空则为attachment否则为传递过来的openstyle的值
            openStyle = openStyle==null?"attachment":openStyle;
            //D:/IdeaProject/demo1/target/classes//static/files/2021-07-20/20210720150121c8dc32294fac4e8eb8579956b599d9b7.png
            // 获取文件名
            String newFileName = path.substring(path.lastIndexOf("/") + 1);
            String time = path.substring(path.indexOf("files") + 6, path.lastIndexOf("/"));
            String classPath = ResourceUtils.getURL("classpath:").getPath() + "/static/files/"+time ;
            //classpath:file:D:/IdeaProject/demo/target/classes//static/files/2021-07-19/真实路径

            File file = new File(classPath,newFileName);
            if (!file.exists()) {
                return R.error().message("文件路径不存在");
            }
            // 创建字节数组
            byte[] buffer = new byte[1024];
            // 获取文件输入流
            is = new FileInputStream(new File(classPath, newFileName));
            // 读文件流提供一个缓冲区
            bis = new BufferedInputStream(is);
            // 请求头设置下载  采用newfilename为下载后的名字
            response.setHeader("content-disposition",openStyle+";fileName="+ URLEncoder.encode(newFileName,"UTF-8"));
            // 输出流
            os = response.getOutputStream();
            // 一次性读取1024字节流 然后转换成int的形式，并将数据存入buffer中
            int i = bis.read(buffer);
            // 如果为-1说明读到最后一个数据
            while (i!=-1) {
                // 字节数组写进流，从0到i个字节
                os.write(buffer,0,i);
                // 强制写入
                os.flush();
                // 读完继续读，直到所有文件流读完返回-1
                i = bis.read(buffer);
            }
            return R.ok().message("文件下载成功");
        } catch (IOException e) {
            e.printStackTrace();
            return R.error().message("文件下载失败");
        } finally {
            // 关闭操作
            if (bis != null) {
                bis.close();
            }
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
    }


    /**
     * 跨服务器下载
     * @param response
     * @param fileName
     * @return
     * @throws Exception
     */
    @GetMapping("/downloadOneFile")
    public R downloadOneFile(HttpServletResponse  response,
                           @RequestParam String fileName) throws Exception{
        InputStream is = null;
        OutputStream os = null;
        // 缓冲区输入流
        BufferedInputStream  bis = null;
        try {
            //得到网络访问对象HttpURLConnection
            HttpURLConnection conn = (HttpURLConnection) new URL("http://192.168.78.141:8088/" + fileName).openConnection();
            //从主机读取数据的超时时间
            conn.setReadTimeout(6000);
            // 连接主机的超时时间
            conn.setConnectTimeout(6000);
            //设置请求方式
            conn.setRequestMethod("GET");
            //连接
            conn.connect();
            //得到响应流
            is = conn.getInputStream();
           //判断响应状态码为200
            if (conn.getResponseCode()==HttpURLConnection.HTTP_OK){
                bis = new  BufferedInputStream(is);
                response.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
                //创建字节数组
                byte[] buffer = new byte[1024];
                // 输出流
                os = response.getOutputStream();
                // 一次性读取1024字节流 然后转换成int的形式，并将数据存入buffer中
                int i = bis.read(buffer);

                // 如果为-1说明读到最后一个数据
                while (i != -1) {
                    // 字节数组写进流，从0到i个字节
                    os.write(buffer,0,i);
                    // 强制写入
                    os.flush();
                    // 读完继续读，直到所有文件流读完返回-1
                    i = bis.read(buffer);  
                }
                //关闭连接
                conn.disconnect();
                return R.ok().message("文件下载成功");
            } else {
                return R.error().message("服务器响应失败,请重试");
            }
        } catch (IOException e) {
            e.printStackTrace();
            return R.error().message("文件下载失败");
        }finally {
            // 关闭操
            if (bis != null){
                bis.close();
            }
            if (is != null){
                is.close();
            }
            if (os != null){
                os.close();
            }
        }
    }




    /**
     * 接收客户端发来的文件流请求
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/upload1")
    public String receiveFile(HttpServletRequest request) throws IOException {
        ServletInputStream is = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        try {
            //获取输入流
            is = request.getInputStream();

              //可以通过管道流  字节 数组输出流在内存中创建一个字节数组缓冲区
//            ByteArrayOutputStream bos = new ByteArrayOutputStream();
//            byte[] buffer1 = new byte[1024];
//            int len;
//            while ((len =is.read(buffer1)) !=-1){
//                bos.write(buffer1,0,len);
//            }
//            System.out.println(bos.toString());
            //放入缓冲区，减少大量的IO请求
            bis = new BufferedInputStream(is);
            //获取文件名（包括目录） 例如： zxl/123.txt       URLDecoder.decode解码
            String fileName = URLDecoder.decode(request.getHeader("fileName"),"UTF-8");
            String filePath = "D:\\newpath";
            //D:\oldpath/zxl/123.txt

            //说明有多重目录  zxl/123.txt
            if (fileName.lastIndexOf("/") !=-1){
                String dir = fileName.substring(0,fileName.lastIndexOf("/"));
                //判断指定文件夹是否存在，不存在则创建
                createDirIFNoExist(filePath + "/" + dir);
            } else {
                //判断指定文件夹是否存在，不存在则创建
                createDirIFNoExist(filePath);
            }
            fos = new FileOutputStream(filePath+"/"+fileName);


            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) !=-1){
                fos.write(buffer,0,len);
            }
            System.out.println("文件名为: "+fileName+"同步成功");
            return "接收数据成功";
        } catch (IOException e) {
            e.printStackTrace();
            return "接收数据失败";
        } finally {
            if (bis!=null){
                bis.close();
            }
            if (is!=null){
                is.close();
            }
            if (fos!=null){
                fos.close();
            }
        }
        //boas.toString()输出字符串
//        System.out.println(boas.toString());

    }


    /**
     * 接收客户端发来的文件流请求(多线程方式)
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/upload2")
    public String receiveFile1(HttpServletRequest request) throws IOException {
        ServletInputStream is = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        try {
            //获取输入流
            is = request.getInputStream();

            //可以通过管道流  字节 数组输出流在内存中创建一个字节数组缓冲区
//            ByteArrayOutputStream bos = new ByteArrayOutputStream();
//            byte[] buffer1 = new byte[1024];
//            int len;
//            while ((len =is.read(buffer1)) !=-1){
//                bos.write(buffer1,0,len);
//            }
//            System.out.println(bos.toString());
            //放入缓冲区，减少大量的IO请求
            bis = new BufferedInputStream(is);
            //获取文件名（包括目录） 例如： zxl/123.txt       URLDecoder.decode解码
            String fileName = URLDecoder.decode(request.getHeader("fileName"),"UTF-8");
            String filePath = "D:\\newpath";
            //D:\oldpath/zxl/123.txt

            //说明有多重目录  zxl/123.txt
            if (fileName.lastIndexOf("\\") !=-1){
                String dir = fileName.substring(0,fileName.lastIndexOf("\\"));
                //判断指定文件夹是否存在，不存在则创建
                createDirIFNoExist(filePath + "/" + dir);
            } else {
                //判断指定文件夹是否存在，不存在则创建
                createDirIFNoExist(filePath);
            }
            fos = new FileOutputStream(filePath+"/"+fileName);


            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) !=-1){
                fos.write(buffer,0,len);
            }
            System.out.println("文件名为: "+fileName+"同步成功");
            return "接收数据成功";
        } catch (IOException e) {
            e.printStackTrace();
            return "接收数据失败";
        } finally {
            if (bis!=null){
                bis.close();
            }
            if (is!=null){
                is.close();
            }
            if (fos!=null){
                fos.close();
            }
        }
        //boas.toString()输出字符串
//        System.out.println(boas.toString());

    }








    /**
     * 用Httpclient方式 redis缓存
     * @param
     * @param response
     * @param multipartFile
     * @param
     */
    @PostMapping("/fileupload")
    public void processUpload(HttpServletResponse response,@RequestParam("fileName") MultipartFile[] multipartFile,String[] name) throws UnsupportedEncodingException {
        String uploadPath = "D:\\newpath\\upload\\";

        for (MultipartFile file : multipartFile) {
            String folder = file.getOriginalFilename();

            if(folder.lastIndexOf("\\") !=-1){
                //获取文件夹目录
                String dir = folder.substring(0, folder.lastIndexOf("\\"));
                //创建目录
                createDirIFNoExist(uploadPath+dir);
            } else {
                //创建目录
                createDirIFNoExist(uploadPath);
            }

            //上传文件
            try {
                file.transferTo(new File(uploadPath+folder));
                //multipartFile会缓存上一个上传的文件，1 12 123..... 不能用addHeader
                response.setHeader("token",URLEncoder.encode("文件"+file.getOriginalFilename()+"上传成功","UTF-8"));
            } catch (IOException e) {
                System.out.println("文件: "+file.getOriginalFilename()+"上传失败！！！");
                response.setHeader("token",URLEncoder.encode("文件"+file.getOriginalFilename()+"上传失败","UTF-8"));
                e.printStackTrace();
            }
        }
        //存入缓存当中   用参数传过来，也会追加上一次传递的文件名，name[name.length-1]每次获取最后一个文件名
        redisTemplate.opsForList().leftPush("fileName",name[name.length-1]);

        //不能放在for循环，因为multipartFile会缓存上一个上传的文件，1 12 123.....
        //用参数传过来，也会追加上一次传递的文件名，name[name.length-1]每次获取最后一个文件名
        System.out.println("文件: "+name[name.length-1]+"上传成功！！！");
    }





    /**
     * 用Httpclient方式 不加redis
     * @param
     * @param response
     * @param multipartFile
     * @param
     */
    @PostMapping("/fileupload1")
    public void processUpload1(HttpServletResponse response,@RequestParam("fileName") MultipartFile[] multipartFile,String[] name) throws UnsupportedEncodingException {
        String uploadPath = "D:\\newpath\\upload\\";

        for (MultipartFile file : multipartFile) {
            String folder = file.getOriginalFilename();

            if(folder.lastIndexOf("\\") !=-1){
                //获取文件夹目录
                String dir = folder.substring(0, folder.lastIndexOf("\\"));
                //创建目录
                createDirIFNoExist(uploadPath+dir);
            } else {
                //创建目录
                createDirIFNoExist(uploadPath);
            }

            //上传文件
            try {
                file.transferTo(new File(uploadPath+folder));
                //multipartFile会缓存上一个上传的文件，1 12 123..... 不能用addHeader
                response.setHeader("token",URLEncoder.encode(file.getOriginalFilename(),"UTF-8"));
            } catch (IOException e) {
                System.out.println("文件: "+file.getOriginalFilename()+"上传失败！！！");
                response.setHeader("token",URLEncoder.encode(file.getOriginalFilename(),"UTF-8"));
                e.printStackTrace();
            }
        }
        System.out.println("文件: "+name[name.length-1]+"上传成功！！！");
    }




    //测试
    @GetMapping("/save")
    public void save (String url){
        redisTemplate.opsForList().leftPush("ad",url);
        redisTemplate.opsForList().leftPush("ad",url);
        redisTemplate.opsForList().leftPush("ad",url);
    }

    //测试
    @GetMapping("get")
    public void get(){
        System.out.println(redisTemplate.opsForList().range("ad",0,-1));
    }




    /**
     * 判断指定文件夹是否存在，不存在则创建
     * @param path
     */
    public void createDirIFNoExist(String path){
        // 检测路径名文件或目录是否存在
        File file = new File(path);
        //不存在则创建
        if (!file.exists()){
            file.mkdirs();
        }

    }



    @GetMapping(value = "/exportWord")
    public void exportWord(String id, HttpServletResponse response){
        try {
//            CallPoliceEntity callPoliceEntity = callPoliceService.selectById(id);
//            Assert.notNull(callPoliceEntity,"没有该报警单号的数据");
//            //实体数据转换
//            ExportDto exportDto = entityTransition(callPoliceEntity);
//            Configuration configuration = new Configuration(Configuration.getVersion());
//            configuration.setClassForTemplateLoading(CallPoliceController.class,"/");
//            Template template = configuration.getTemplate("model.xml", "UTF-8");
//            String result = FreeMarkerTemplateUtils.processTemplateIntoString(template, exportDto);
//            FileUtil.exportWord(result,String.format("报警回执%s",".doc"),response);
        }catch (Exception e){
            log.error("导出文件失败{}",e.getMessage());
        }
    }


    @PostMapping("/upload")
    public void uploadFile(MultipartFile file){
        String filename = file.getOriginalFilename();

        Assert.isTrue(StringUtils.isNotEmpty(filename),"获取文件名称失败");
        File file1 = new File(binSavePath, filename);
        if (!file1.getParentFile().exists()){
            file1.getParentFile().mkdirs();
        }
        Assert.isTrue(!file1.exists(),"文件已存在");
        log.info("文件路径=【{}】",file1.getAbsoluteFile());
        try {
            file.transferTo(file1.getAbsoluteFile());
        } catch (IOException e) {
            log.error("{}",e.getMessage());
            Assert.isTrue(false,"文件上传失败");
        }
    }


    @PostMapping("/downLoad")
    public void downLoad(String fileName){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null == requestAttributes || null == requestAttributes.getResponse()) {
            return;
        }
        HttpServletResponse response = requestAttributes.getResponse();
        File file = Paths.get(binSavePath, fileName).toFile();
        try {
            boolean isFile = FileUtil.isFile(file);
            ServletOutputStream outputStream = response.getOutputStream();
            //文件不存在
            if (!isFile) {
                R error = R.error();
                String resultJson = JSON.toJSONString(error);
                response.setStatus(HttpStatus.HTTP_NOT_FOUND);
                response.setHeader(HttpHeaders.CONNECTION, "keep-alive");
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                outputStream.write(resultJson.getBytes());
                outputStream.flush();
                outputStream.close();
                return;
            }
        } catch (Exception e) {
            log.error("下载文件", e);
        }

        try (FileInputStream fileInputStream = new FileInputStream(file);
             OutputStream outputStream = response.getOutputStream();
        ) {
            // 设置信息给客户端不解析
            String type = new MimetypesFileTypeMap().getContentType(fileName);
            response.setContentType("application/octet-stream");
            response.setContentLength(fileInputStream.available());
            // 设置contenttype，即告诉客户端所发送的数据属于什么类型
            // 设置编码
            String filename = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
            // 设置扩展头，当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + filename);

            byte[] buffer = new byte[1024];
            int length;
            while ((length = fileInputStream.read(buffer)) > 0){
                outputStream.write(buffer, 0, length);
            }
            outputStream.flush();
            IoUtil.copy(fileInputStream, outputStream);
        } catch (Exception e) {
            log.error("下载文件", e);
        }
    }





    @PostMapping("/test")
    public String test1(JobBean jobBean){
        return "{\"status\":\"success\"}";
    }


    public static void main(String[] args) {
//        String path = "D:/IdeaProject/demo1/target/classes//static/files/2021-07-20/20210720150121c8dc32294fac4e8eb8579956b599d9b7.png";
//        // 获取文件名
//        String newfilename = path.substring(path.lastIndexOf("/") + 1);
//        String files = path.substring(path.indexOf("files") + 6, path.lastIndexOf("/"));
//        System.out.println("newfile:"+newfilename);
//        System.out.println("files:"+files);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        System.out.println(calendar.get(Calendar.YEAR));
        System.out.println(calendar.get(Calendar.MONTH) +1);
        System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
        System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
        System.out.println(calendar.get(Calendar.MINUTE));



    }

}
